package com.liming.auth.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.liming.auth.mapper.AdminAuthMapper;
import com.liming.auth.service.*;
import com.liming.common.constant.RedisConstants;
import com.liming.common.constant.TimeConstants;
import com.liming.common.enums.LockedEnum;
import com.liming.common.exception.BusinessException;
import com.liming.common.exception.LoginException;
import com.liming.common.pojo.dto.authinfo.AdminAddOrUpdateDTO;
import com.liming.common.pojo.dto.authinfo.AdminLoginDTO;
import com.liming.common.pojo.dto.authinfo.UserPageQuery;
import com.liming.common.pojo.entity.authinfo.*;
import com.liming.common.pojo.vo.PageVO;
import com.liming.common.pojo.vo.authinfo.*;
import com.liming.common.util.JwtUtils;
import com.liming.common.util.ThreadLocalUtils;
import com.liming.common.util.mybatisplus.MPUtil;
import com.liming.service.redis.RedisClient;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.liming.common.constant.DBConstants.UNLOCKED;
import static com.liming.common.constant.SystemConstants.*;

/**
 * @author liming
 * @date 2025/1/14
 */
@Slf4j
@Service
@AllArgsConstructor
public class AdminAuthServiceImpl extends ServiceImpl<AdminAuthMapper, Admin>
        implements AdminAuthService, MPUtil<Admin> {

    private final RedisClient redisClient;
    private final AdminRoleService adminRoleService;
    private final RoleService roleService;
    private final RolePermissionService rolePermissionService;
    private final PermissionService permissionService;
    private final MenuService menuService;
    private final RoleMenuService roleMenuService;
    private final RegionService regionService;
    private final UserAuthService userAuthService;

    @Override
    public AdminLoginVO login(AdminLoginDTO dto) {
        // 1.判断管理员使用用户名登录还是手机号登录 如果是手机号 则使用手机号登录
        String account = dto.getAccount();
        boolean isMobile = Validator.isMobile(account);
        AdminLoginVO vo = new AdminLoginVO();
        log.info("管理员是否使用手机号登录？- {}", isMobile);
        if (isMobile) {
            // 2.手机号登录
            Admin admin = lambdaQuery().eq(Admin::getPhone, account).one();
            BeanUtils.copyProperties(admin, vo);
            vo.setToken(judgeAdminGetToken(admin, "不存在该管理员 - 手机号登录", dto));
            return vo;
        }
        // 3.用户名登录
        Admin admin = lambdaQuery().eq(Admin::getUsername, account).one();
        BeanUtils.copyProperties(admin, vo);
        vo.setToken(judgeAdminGetToken(admin, "不存在该管理员 - 用户名登录", dto));
        return vo;
    }

    private static String getSecurePwdByPwd(String pwd) {
        return SecureUtil.md5().digestHex16(pwd);
    }

    @NotNull
    private String judgeAdminGetToken(Admin admin, String message, AdminLoginDTO dto) {
        // 账户是否存在
        Assert.notNull(admin, () -> new LoginException(message));
        // 账户是否锁定
        Assert.equals(admin.getLocked(), UNLOCKED, () -> new LoginException("账号被锁定"));
        // 密码校验
        Assert.equals(admin.getPassword(), getSecurePwdByPwd(dto.getPassword()), () -> new LoginException("管理员密码错误"));
        // Token
        String token = JwtUtils.getToken(KOI, ADMIN, ID, admin.getId().toString());
        redisClient.valueSet(RedisConstants.ADMIN_KEY + admin.getId().toString(), token,
                TimeConstants.TOKEN_EXPIRE_TIME, TimeUnit.MILLISECONDS);
        return token;
    }

    @Override
    public Boolean logout() {
        String id = ThreadLocalUtils.get(ID);
        if (ObjectUtil.isNull(id)) {
            throw new BusinessException("id为空");
        }
        String token = (String) redisClient.valueGet(RedisConstants.ADMIN_KEY + id);
        JwtUtils.invalid(token);
        return redisClient.delete(RedisConstants.ADMIN_KEY + id);
    }

    @Override
    public List<AdminVO> listAdminVO() {
        return list().stream().map(admin -> {
            AdminVO vo = new AdminVO();
            List<RoleVO> roleList = adminRoleList(admin.getId());
            BeanUtils.copyProperties(admin, vo);
            vo.setRoles(roleList);
            return vo;
        }).collect(Collectors.toList());
    }

    private static Boolean idsIsEmpty(List<Long> ids, AdminAuthVO result,
                                      List<String> roleNameList, List<String> permissionNameList, List<MenuVO> menuList) {
        if (CollUtil.isEmpty(ids)) {
            result.setRoleList(roleNameList);
            result.setPermissionList(permissionNameList);
            result.setMenuList(menuList);
            return true;
        }
        return false;
    }

    @Override
    public AdminAuthVO auth() {
        String adminId = ThreadLocalUtils.get(ID);
        AdminAuthVO result = new AdminAuthVO();
        List<String> roleNameList = Collections.emptyList();
        List<String> permissionNameList = Collections.emptyList();
        List<MenuVO> menuList = Collections.emptyList();
        // 角色
        List<Long> roleIds = adminRoleService.roleIdsByAdminId(adminId);
        if (ObjectUtil.equal(idsIsEmpty(roleIds, result, roleNameList, permissionNameList, menuList), Boolean.TRUE)) {
            return result;
        }
        roleNameList = roleService.listByIds(roleIds).stream().map(Role::getName).toList();
        Assert.notEmpty(roleNameList, () -> new BusinessException("角色不存在"));

        // 菜单
        List<Long> menuIds = roleMenuService.menuIdsByRoleIds(roleIds);
        if (ObjectUtil.equal(idsIsEmpty(menuIds, result, roleNameList, permissionNameList, menuList), Boolean.TRUE)) {
            return result;
        }
        menuList = menuService.menuByMenuIds(menuIds);

        // 权限
        List<Long> permissionIds = rolePermissionService.permissionIdsByRoleIds(roleIds);
        if (ObjectUtil.equal(idsIsEmpty(permissionIds, result, roleNameList, permissionNameList, menuList), Boolean.TRUE)) {
            return result;
        }
        permissionNameList = permissionService.listByIds(permissionIds).stream().map(Permission::getName).toList();
        Assert.notEmpty(permissionNameList, () -> new BusinessException("权限不存在"));

        result.setRoleList(roleNameList);
        result.setPermissionList(permissionNameList);
        result.setMenuList(menuList);
        return result;
    }

    @Override
    public String getNameAndPhone(Long id) {
        Admin admin = getById(id);
        return StrUtil.builder().append(admin.getUsername()).append("/").append(admin.getPhone()).toString();
    }

    @Override
    public Boolean addRole(String roleName) {
        Role role = new Role();
        role.setName(roleName);
        Assert.isTrue(roleService.save(role), () -> new BusinessException("新增角色失败"));
        return true;
    }

    @Override
    public Boolean addPermission(String name, String description) {
        Permission permission = new Permission();
        permission.setName(name);
        permission.setDescription(description);
        Assert.isTrue(permissionService.save(permission), () -> new BusinessException("新增权限失败"));
        return true;
    }

    @Override
    public Boolean adminRole(Long adminId, List<Long> roleIds) {
        adminRoleService.lambdaUpdate().eq(AdminRole::getAdminId, adminId).remove();
        if (CollUtil.isEmpty(roleIds)) {
            return true;
        }
        List<AdminRole> adminRoles = roleIds.stream().map(roleId -> {
            AdminRole adminRole = new AdminRole();
            adminRole.setAdminId(adminId);
            adminRole.setRoleId(roleId);
            return adminRole;
        }).toList();
        Assert.isTrue(adminRoleService.saveBatch(adminRoles), () -> new BusinessException("用户角色新增失败"));
        return true;
    }

    @Override
    public Boolean rolePermission(Long roleId, List<Long> permissionIds) {
        rolePermissionService.lambdaUpdate().eq(RolePermission::getRoleId, roleId).remove();
        if (CollUtil.isEmpty(permissionIds)) {
            return true;
        }
        List<RolePermission> rolePermissions = permissionIds.stream().map(permissionId -> {
            RolePermission rolePermission = new RolePermission();
            rolePermission.setRoleId(roleId);
            rolePermission.setPermissionId(permissionId);
            return rolePermission;
        }).toList();
        Assert.isTrue(rolePermissionService.saveBatch(rolePermissions), () -> new BusinessException("角色权限新增失败"));
        return true;
    }

    @Override
    public Boolean updateStatus(Long id) {
        Admin admin = getById(id);
        Assert.notNull(admin, () -> new BusinessException("管理员不存在"));
        Admin update = new Admin();
        update.setId(id);
        if (ObjectUtil.equal(admin.getLocked(), LockedEnum.UNLOCKED.getCode())) {
            update.setLocked(LockedEnum.LOCKED.getCode());
        } else {
            update.setLocked(LockedEnum.UNLOCKED.getCode());
        }
        Assert.isTrue(updateById(update), () -> new BusinessException("管理员锁定状态更新失败"));
        return true;
    }

    @Override
    public Boolean saveAdmin(AdminAddOrUpdateDTO dto) {
        // 用户名称 - 手机号 不允许重复
        checkNameUnique(dto.getUsername(), dto.getId());
        checkPhoneUnique(dto.getPhone(), dto.getId());
        Admin admin = new Admin();
        BeanUtils.copyProperties(dto, admin);
        admin.setPassword(getSecurePwdByPwd(dto.getPassword()));
        admin.setLocked(LockedEnum.UNLOCKED.getCode());
        Assert.isTrue(save(admin), () -> new BusinessException("新增管理员失败"));
        return true;
    }

    private void checkPhoneUnique(String phone, Long id) {
        Admin check = lambdaQuery()
                .eq(Admin::getPhone, phone)
                .ne(ObjectUtil.isNotNull(id), Admin::getId, id)
                .one();
        Assert.isNull(check, () -> new BusinessException("手机号已存在"));
    }

    private void checkNameUnique(String username, Long id) {
        Admin check = lambdaQuery()
                .eq(Admin::getUsername, username)
                .ne(ObjectUtil.isNotNull(id), Admin::getId, id)
                .one();
        Assert.isNull(check, () -> new BusinessException("用户名称已存在"));
    }

    @Override
    public Boolean updateAdmin(AdminAddOrUpdateDTO dto) {
        checkNameUnique(dto.getUsername(), dto.getId());
        checkPhoneUnique(dto.getPhone(), dto.getId());
        Admin update = new Admin();
        BeanUtils.copyProperties(dto, update);
        update.setPassword(getSecurePwdByPwd(dto.getPassword()));
        Assert.isTrue(updateById(update), () -> new BusinessException("更新管理员信息失败"));
        return true;
    }

    @Override
    public List<RoleVO> roleList() {
        return roleService.list().stream().map(entity -> {
            RoleVO vo = new RoleVO();
            List<PermissionVO> permissionList = rolePermissionList(entity.getId());
            BeanUtils.copyProperties(entity, vo);
            vo.setPermissions(permissionList);
            return vo;
        }).toList();
    }

    @Override
    public List<PermissionVO> permissionList() {
        return permissionService.list().stream().map(permission -> {
            PermissionVO vo = new PermissionVO();
            List<RoleVO> roles = roleService.usePermissionRoles(permission.getId());
            BeanUtils.copyProperties(permission, vo);
            vo.setUsePermissionRoles(roles);
            return vo;
        }).toList();
    }

    @Override
    public Boolean removeRoleById(Long id) {
        Assert.isTrue(roleService.removeById(id), () -> new BusinessException("角色删除失败"));
        // 删除管理员对应的角色关联
        Assert.isTrue(adminRoleService.removeByRoleId(id), () -> new BusinessException("角色对应相关管理员关联删除失败"));
        // 删除角色对应的权限关联
        Assert.isTrue(rolePermissionService.removeByRoleId(id), () -> new BusinessException("角色对应相关权限关联删除失败"));
        return true;
    }

    @Override
    public Boolean removePermissionById(Long id) {
        Assert.isTrue(permissionService.removeById(id), () -> new BusinessException("权限删除失败"));
        // 删除权限对应的角色关联
        Assert.isTrue(rolePermissionService.removeByPermissionId(id), () -> new BusinessException("权限对应相关角色关联删除失败"));
        return true;
    }

    @Override
    public List<Region> regionList(Long parentId, Integer level) {
        return regionService.lambdaQuery().eq(Region::getParentId, parentId).eq(Region::getLevel, level).list();
    }

    @Override
    public PageVO<UserVO> userPage(UserPageQuery query) {
        return userAuthService.adminUserPage(query);
    }

    @Override
    public List<RoleVO> adminRoleList(Long adminId) {
        List<Long> roleIdList = adminRoleService.listByAdminId(adminId);
        if (CollUtil.isEmpty(roleIdList)) {
            return Collections.emptyList();
        }
        return roleService.listByIds(roleIdList).stream().map(role -> {
            RoleVO vo = new RoleVO();
            BeanUtils.copyProperties(role, vo);
            return vo;
        }).toList();
    }

    @Override
    public List<PermissionVO> rolePermissionList(Long roleId) {
        List<Long> permissionIdList = rolePermissionService.listByRoleId(roleId);
        if (CollUtil.isEmpty(permissionIdList)) {
            return Collections.emptyList();
        }
        return permissionService.listByIds(permissionIdList).stream().map(entity -> {
            PermissionVO vo = new PermissionVO();
            BeanUtils.copyProperties(entity, vo);
            return vo;
        }).toList();
    }

}
